package com.stottlerhenke.simbionic.engine.parser;
import com.stottlerhenke.simbionic.common.parser.EUnaryOp;
import com.stottlerhenke.simbionic.common.parser.SB_ExpressionNode;
import com.stottlerhenke.simbionic.engine.core.*;
import com.stottlerhenke.simbionic.api.SB_Exception;
import com.stottlerhenke.simbionic.api.SB_VarType;


public class SB_ExpUnaryOp extends SB_Expression {
  protected EUnaryOp _op;
  protected SB_Expression _arg;

  public SB_ExpUnaryOp(SB_ExpressionNode arg, EUnaryOp op) {
    super();
    _arg = (SB_Expression)arg;
    _op = op;
  }
  public EExpressionType GetType(){ return EExpressionType.kUnary; }

  /**
   * Replaces all instances of SB_Method in the expression tree that
   * match the name of newMethod.
   * @param newMethod the method with which to replace existing methods
   */
  public void replaceFunction(SB_Function newMethod)
  {
      _arg.replaceFunction(newMethod);
  }
  
  public SB_Variable Evaluate(SB_Entity entity, SB_ExecutionFrame contextFrame) throws SB_Exception {
    SB_Variable arg = _arg.Evaluate(entity, contextFrame);
    SB_Variable result;

    if(_op == EUnaryOp.kNotOp)
      result = arg.opNOT();
    else if(_op == EUnaryOp.kNegateOp)
      result = arg.opNEG();
    else if(_op == EUnaryOp.kParenOp)
      result = arg.Clone();
    else if(_op == EUnaryOp.kCastIntOp)
    {
        if (arg.GetType() == SB_VarType.kFloat)
            result = new SB_VarInteger( (int)((SB_VarFloat)arg).GetValue() );
        else if (arg.GetType() == SB_VarType.kEntity)
            result = new SB_VarInteger( (int)((SB_VarEntity)arg).GetValue()._id );
        else 
            result = new SB_VarInteger( convertToNumber(arg).intValue() );
    }
    else if(_op == EUnaryOp.kCastFloatOp)
    {
        if (arg.GetType() == SB_VarType.kInteger)
            result = new SB_VarFloat( (float)((SB_VarInteger)arg).GetValue() );
        else if (arg.GetType() == SB_VarType.kEntity)
            result = new SB_VarFloat( (float)((SB_VarEntity)arg).GetValue()._id );
        else 
            result = new SB_VarFloat( convertToNumber(arg).floatValue() );
    }
    else if(_op == EUnaryOp.kCastDoubleOp)
    {
        double value;
        if (arg.GetType() == SB_VarType.kInteger)
            value = ((SB_VarInteger)arg).GetValue();
        else if (arg.GetType() == SB_VarType.kFloat)
            value = ((SB_VarFloat)arg).GetValue();
        else if (arg.GetType() == SB_VarType.kEntity)
            value = ((SB_VarEntity)arg).GetValue()._id;
        else 
            value = convertToNumber(arg).doubleValue();

        Double dbl = new Double( value );
        SB_VarClass obj = new SB_VarClass();
        obj.setClassObject(dbl);
        result = obj;
    }
    else if(_op == EUnaryOp.kCastShortOp)
    {
        short value;
        if (arg.GetType() == SB_VarType.kInteger)
            value = (short)((SB_VarInteger)arg).GetValue();
        else if (arg.GetType() == SB_VarType.kFloat)
            value = (short)((SB_VarFloat)arg).GetValue();
        else if (arg.GetType() == SB_VarType.kEntity)
            value = (short)((SB_VarEntity)arg).GetValue()._id;
        else 
            value = convertToNumber(arg).shortValue();

        Short shrt = new Short( value );
        SB_VarClass obj = new SB_VarClass();
        obj.setClassObject(shrt);
        result = obj;
    }
    else if(_op == EUnaryOp.kCastByteOp)
    {
        byte value;
        if (arg.GetType() == SB_VarType.kInteger)
            value = (byte)((SB_VarInteger)arg).GetValue();
        else if (arg.GetType() == SB_VarType.kFloat)
            value = (byte)((SB_VarFloat)arg).GetValue();
        else if (arg.GetType() == SB_VarType.kEntity)
            value = (byte)((SB_VarEntity)arg).GetValue()._id;
        else 
            value = convertToNumber(arg).byteValue();

        Byte b = new Byte( value );
        SB_VarClass obj = new SB_VarClass();
        obj.setClassObject(b);
        result = obj;
    }
    else if(_op == EUnaryOp.kCastLongOp)
    {
        long value;
        if (arg.GetType() == SB_VarType.kInteger)
            value = ((SB_VarInteger)arg).GetValue();
        else if (arg.GetType() == SB_VarType.kFloat)
            value = (long)((SB_VarFloat)arg).GetValue();
        else 
            value = convertToNumber(arg).longValue();

        Long lng = new Long( value );
        SB_VarClass obj = new SB_VarClass();
        obj.setClassObject(lng);
        result = obj;
    }
    else result = null;

    return result;
  }
  
  private Number convertToNumber(SB_Variable var) throws SB_Exception
  {
      try
      {
          return (Number)((SB_VarClass)var).getClassObject();
      }
      catch (ClassCastException e)
      {
          throw new SB_Exception("Illegal casting operation on '" + var.toString() + "'");
      }      
  }
  
  public String PrintTree() throws SB_Exception{
    if(_op == EUnaryOp.kNotOp)
      return new String(" ~ " + _arg.PrintTree());
    else if(_op == EUnaryOp.kNegateOp)
      return new String(" - " + _arg.PrintTree());
    else if(_op == EUnaryOp.kParenOp)
      return new String("(" + _arg.PrintTree() + ")");
    else if (_op == EUnaryOp.kCastIntOp)
      return new String("(int)");
    else if (_op == EUnaryOp.kCastFloatOp)
      return new String("(float)");
    else
      return "";
  }
}